use clap::{Parser, Subcommand, ArgAction};
use importer::movie::{Movie, SearchMovie};
use interface::handle::{Interface, ModifyMovie};
use movie::error::Result;

/// 这个派生宏可以把 Cli 的字段转化为命令行参数
#[derive(Parser)]
/// 这个派生宏用来设置 Cli 的一些元信息
#[command(
    version, //  cargo run -- --version
    about = "Movie app", //  cargo run -- -h
    long_about = "Run Movie CLI") //  cargo run -- --help
]
struct Cli {
    #[clap(subcommand)] // 标识一下是子命令
    commands: Option<Commands>,
}

/// 子命令
#[derive(Subcommand, Debug)]
enum Commands {

    /// 用户登录系统
    ///
    /// 示例:
    /// - 登录帮助：
    ///   cargo run -- login -h
    /// - 使用用户名登录：
    ///   cargo run -- login -u root
    /// - 使用长选项指定用户名：
    ///   cargo run -- login --username admin
    ///
    /// 说明：
    /// 登录后可执行后续的查询、添加、修改、删除等操作
    Login {
        /// 登录用户名（系统验证身份的标识）
        /// 示例：-u root 表示使用root用户登录
        #[arg(short = 'u', long = "username", default_value = "", help_heading = "登录参数")]
        username: String,
    },
    /// 转换DVD数据文件为JSON格式（更新数据文件路径）
    ///
    /// 示例:
    /// - 转换帮助：
    ///   cargo run -- change -h
    /// - 指定DVD数据文件路径并转换：
    ///   cargo run -- change -p /root/rust_workspace/movie/DVDs.txt
    /// - 使用长选项指定路径：
    ///   cargo run -- change --path /data/movies/DVDs.txt
    ///
    /// 说明：
    /// 1. 该命令会将指定的DVD文本文件转换为程序可识别的JSON格式
    /// 2. 转换后的数据文件路径将被保存，后续操作默认使用该路径
    Change {
        /// DVD数据文件的本地路径（需包含完整文件路径）
        /// 示例：-p /data/DVDs.txt 表示转换该路径下的DVD文件
        #[arg(short = 'p', long = "path", default_value = "", help_heading = "文件路径参数")]
        path: String,
    },
    /// 按条件查询电影记录并分页显示
    ///
    /// 示例:
    /// - 查询帮助：
    ///   cargo run -- select -h
    /// - 查询碟片1中标题包含"蝙蝠侠"的记录（第1页，每页10条）：
    ///   cargo run -- select -d 1 -D "==" -t "蝙蝠侠" -T "contains" -p 1 -s 10
    /// - 查询碟片1中的所有记录（第1页，每页20条）：
    ///   cargo run -- select -d 1 -D "==" -p 1 -s 20
    /// - 查询所有记录（默认第1页，每页10条）：
    ///   cargo run -- select -p 1 -s 10
    ///
    /// 说明：
    /// 1. 筛选条件可组合使用，未指定的条件（如未传 -d 和 -D）视为不筛选该字段
    /// 2. 分页参数默认：第1页（-p 1），每页10条（-s 10）
    Select {
        /// 筛选条件：碟片号（数值），需配合 -D 使用
        /// 示例：-d 1 表示查询碟片号为1的记录
        #[arg(short = 'd', long, default_value_t = 0, help_heading = "筛选条件（数值型）")]
        disc: u32,
        /// 碟片号的匹配条件，需配合 -d 使用
        /// 支持：==（等于）、!=（不等于）、<（小于）、>（大于）、<=（小于等于）、>=（大于等于）
        #[arg(short = 'D', long, default_value = "", help_heading = "操作符（数值型）")]
        disc_op: String,
        /// 筛选条件：电影发行年份（字符串），需配合 -Y 使用
        /// 示例：-y "2023" 表示查询年份为2023的记录
        #[arg(short = 'y', long, default_value = "", help_heading = "筛选条件（字符串型）")]
        year: String,
        /// 年份的匹配条件，需配合 -y 使用
        /// 支持：==（等于）、!=（不等于）、contains（包含）、starts_with（前缀）、ends_with（后缀）
        #[arg(short = 'Y', long, default_value = "", help_heading = "操作符（字符串型）")]
        year_op: String,
        /// 筛选条件：电影名称（字符串），需配合 -T 使用
        /// 示例：-t "蝙蝠侠" 表示查询标题含"蝙蝠侠"的记录
        #[arg(short = 't', long, default_value = "", help_heading = "筛选条件（字符串型）")]
        title: String,
        /// 标题的匹配条件，需配合 -t 使用
        /// 支持：==（等于）、!=（不等于）、contains（包含）、starts_with（前缀）、ends_with（后缀）
        #[arg(short = 'T', long, default_value = "", help_heading = "操作符（字符串型）")]
        title_op: String,
        /// 筛选条件：电影标签（字符串），需配合 -R 使用
        /// 示例：-r "经典" 表示查询备注为"经典"的记录
        #[arg(short = 'r', long, default_value = "", help_heading = "筛选条件（字符串型）")]
        remark: String,
        /// 备注的匹配条件，需配合 -r 使用
        /// 支持：==（等于）、!=（不等于）、is_none（无备注）、is_some（有备注）
        #[arg(short = 'R', long, default_value = "", help_heading = "操作符（备注专用）")]
        remark_op: String,
        /// 筛选条件：碟片内序号（数值），需配合 -N 使用
        /// 示例：-n 3 表示查询碟片内序号为3的记录
        #[arg(short = 'n', long, default_value_t = 0, help_heading = "筛选条件（数值型）")]
        no: u32,
        /// 序号的匹配条件，需配合 -n 使用
        /// 支持：==（等于）、!=（不等于）、<（小于）、>（大于）、<=（小于等于）、>=（大于等于）
        #[arg(short = 'N', long, default_value = "", help_heading = "操作符（数值型）")]
        no_op: String,
        /// 分页参数：页码（从1开始）
        /// 示例：-p 2 表示查询第2页结果
        #[arg(short = 'p', long, default_value_t = 1, help_heading = "分页参数")]
        page: u32,
        /// 分页参数：每页显示条数
        /// 示例：-s 20 表示每页显示20条记录
        #[arg(short = 's', long, default_value_t = 10, help_heading = "分页参数")]
        size: u32,
    },
    /// 修改符合条件的电影记录
    ///
    /// 示例:
    /// - 修改帮助：
    ///   cargo run -- update -h
    /// - 将碟片1中2006年的记录备注改为"test"：
    ///   cargo run -- update -d 1 -D "==" -y "2006" -Y "==" --new-remark "test"
    /// - 将标题为"蝙蝠侠"的记录改名为"新蝙蝠侠"：
    ///   cargo run -- update -t "蝙蝠侠" -T "==" --new-title "新蝙蝠侠"
    /// - 将标题为"小丑"的记录移至碟片3：
    ///   cargo run -- update -t "小丑" -T "==" --new-disc 3
    ///
    /// 说明：
    /// 1. 筛选条件（-d、-y、-t等）可组合使用，未指定的条件视为不筛选
    /// 2. 修改字段（--new-disc等）至少指定一个，未指定的字段保持不变
    Update {
        /// 筛选条件：碟片号（数值），需配合 -D 使用
        /// 示例：-d 1 表示筛选碟片号为1的记录
        #[arg(short = 'd', long, default_value_t = 0, help_heading = "筛选条件（数值型）")]
        disc: u32,
        /// 碟片号的操作符，需配合 -d 使用
        /// 支持：==, !=, <, >, <=, >=
        #[arg(short = 'D', long, default_value = "", help_heading = "操作符（字符串型）")]
        disc_op: String,
        /// 筛选条件：年份（字符串），需配合 -Y 使用
        /// 示例：-y "2023" 表示筛选年份为2023的记录
        #[arg(short = 'y', long, default_value = "", help_heading = "筛选条件（字符串型）")]
        year: String,
        /// 年份的操作符，需配合 -y 使用
        /// 支持：==, !=, contains, starts_with, ends_with
        #[arg(short = 'Y', long, default_value = "", help_heading = "操作符（字符串型）")]
        year_op: String,
        /// 筛选条件：标题（字符串），需配合 -T 使用
        /// 示例：-t "蝙蝠侠" 表示筛选标题为蝙蝠侠的记录
        #[arg(short = 't', long, default_value = "", help_heading = "筛选条件（字符串型）")]
        title: String,
        /// 标题的操作符，需配合 -t 使用
        /// 支持：==, !=, contains, starts_with, ends_with
        #[arg(short = 'T', long, default_value = "", help_heading = "操作符（字符串型）")]
        title_op: String,
        /// 筛选条件：备注（字符串），需配合 -R 使用
        /// 示例：-r "经典" 表示筛选备注为经典的记录
        #[arg(short = 'r', long, default_value = "", help_heading = "筛选条件（字符串型）")]
        remark: String,
        /// 备注的操作符，需配合 -r 使用
        /// 支持：==, !=, is_none, is_some
        #[arg(short = 'R', long, default_value = "", help_heading = "操作符（备注专用）")]
        remark_op: String,
        /// 筛选条件：序号（数值），需配合 -N 使用
        /// 示例：-n 5 表示筛选序号为5的记录
        #[arg(short = 'n', long, default_value_t = 0, help_heading = "筛选条件（数值型）")]
        no: u32,
        /// 序号的操作符，需配合 -n 使用
        /// 支持：==, !=, <, >, <=, >=
        #[arg(short = 'N', long, default_value = "", help_heading = "操作符（数值型）")]
        no_op: String,
        /// 要修改的新碟片号（可选）
        /// 示例：--new-disc 2 表示将匹配记录的碟片号改为2
        #[arg(long = "new-disc", help_heading = "修改字段（至少指定一个）")]
        new_disc: Option<u32>,
        /// 要修改的新年份（可选）
        /// 示例：--new-year "2024" 表示将匹配记录的年份改为2024
        #[arg(long = "new-year", help_heading = "修改字段（至少指定一个）")]
        new_year: Option<String>,
        /// 要修改的新标题（可选）
        /// 示例：--new-title "黑暗骑士" 表示将匹配记录的标题改为黑暗骑士
        #[arg(long = "new-title", help_heading = "修改字段（至少指定一个）")]
        new_title: Option<String>,
        /// 要修改的新备注（可选，输入"none"表示清空备注）
        /// 示例：--new-remark "DC宇宙" 表示将匹配记录的备注改为DC宇宙；--new-remark "none" 表示清空备注
        #[arg(long = "new-remark", help_heading = "修改字段（至少指定一个）")]
        new_remark: Option<String>,
    },
    /// 删除符合条件的电影记录
    ///
    /// 示例:
    /// - 删除帮助：
    ///   cargo run -- delete -h
    /// - 删除碟片1中所有记录：
    ///   cargo run -- delete -d 1 -D "=="
    /// - 删除2007年及之前、备注为"test"的记录：
    ///   cargo run -- delete -d 1 -D "==" -y "2007" -Y "<=" -r "test" -R "=="
    /// - 删除标题包含"蝙蝠侠"的记录：
    ///   cargo run -- delete -t "蝙蝠侠" -T "contains"
    Delete {
        /// 碟片号（数值），配合 -D 使用
        /// 示例：-d 1 表示筛选碟片号为1的记录
        #[arg(short = 'd', long, default_value_t = 0, help_heading = "筛选条件（数值型）")]
        disc: u32,
        /// 碟片号的操作符，配合 -d 使用
        /// 支持：==, !=, <, >, <=, >=
        #[arg(short = 'D', long, default_value = "", value_enum, help_heading = "操作符（字符串型）")]
        disc_op: String,
        /// 年份（字符串），配合 -Y 使用
        /// 示例：-y "2023" 表示筛选年份为2023的记录
        #[arg(short = 'y', long, default_value = "", help_heading = "筛选条件（字符串型）")]
        year: String,
        /// 年份的操作符，配合 -y 使用
        /// 支持：==, !=, contains, starts_with, ends_with
        #[arg(short = 'Y', long, default_value = "", value_enum, help_heading = "操作符（字符串型）")]
        year_op: String,
        /// 标题（字符串），配合 -T 使用
        /// 示例：-t "蝙蝠侠" 表示筛选标题含"蝙蝠侠"的记录
        #[arg(short = 't', long, default_value = "", help_heading = "筛选条件（字符串型）")]
        title: String,
        /// 标题的操作符，配合 -t 使用
        /// 支持：==, !=, contains, starts_with, ends_with
        #[arg(short = 'T', long, default_value = "", value_enum, help_heading = "操作符（字符串型）")]
        title_op: String,
        /// 备注（字符串），配合 -R 使用
        /// 示例：-r "经典" 表示筛选备注为"经典"的记录
        #[arg(short = 'r', long, default_value = "", help_heading = "筛选条件（字符串型）")]
        remark: String,
        /// 备注的操作符，配合 -r 使用
        /// 支持：==, !=, contains, starts_with, ends_with, is_none, is_some
        #[arg(short = 'R', long, default_value = "", value_enum, help_heading = "操作符（备注专用）")]
        remark_op: String,
        /// 序号（数值），配合 -N 使用
        /// 示例：-n 3 表示筛选序号为3的记录
        #[arg(short = 'n', long, default_value_t = 0, help_heading = "筛选条件（数值型）")]
        no: u32,
        /// 序号的操作符，配合 -n 使用
        /// 支持：==, !=, <, >, <=, >=
        #[arg(short = 'N', long, default_value = "", value_enum, help_heading = "操作符（字符串型）")]
        no_op: String,
    },
    /// 批量添加电影记录
    ///
    /// 示例:
    /// - 添加帮助：
    ///   cargo run -- insert -h
    /// - 批量添加3部电影到碟片1（2006年，备注均为"test"）：
    ///   cargo run -- insert -d 1 -y "2006" -t "泰坦尼克号1" -r "test" -d 1 -y "2006" -t "泰坦尼克号2" -r "test" -d 1 -y "2006" -t "泰坦尼克号3" -r "test"
    /// - 添加2部电影（分别在碟片1和2，无备注）：
    ///   cargo run -- insert -d 1 -d 2 -y "2023" -y "2024" -t "电影A" -t "电影B"
    ///
    /// 说明：
    /// 1. 参数需按顺序对应：第1个-d对应第1个-y、第1个-t（可选第1个-r），以此类推
    /// 2. 必选参数：-d（碟片号）、-y（年份）、-t（标题），数量必须一致
    /// 3. 可选参数：-r（备注），数量可少于电影总数（不足则默认无备注）
    Insert {
        /// 碟片号（按顺序对应每部电影，需重复使用-d指定）
        /// 示例：-d 1 -d 2 表示第1部在碟片1，第2部在碟片2
        #[arg(short = 'd', long = "disc", required = true, action = ArgAction::Append, value_name = "DISC", help_heading = "必选参数（数量需一致）")]
        disc: Vec<u32>,
        /// 电影发行年份（按顺序对应每部电影，需重复使用-y指定）
        /// 示例：-y "2023" -y "2024" 表示第1部年份2023，第2部2024
        #[arg(short = 'y', long = "year", required = true, action = ArgAction::Append, value_name = "YEAR", help_heading = "必选参数（数量需一致）")]
        year: Vec<String>,
        /// 电影名称（按顺序对应每部电影，需重复使用-t指定）
        /// 示例：-t "电影A" -t "电影B" 表示第1部标题为电影A，第2部为电影B
        #[arg(short = 't', long = "title", required = true, action = ArgAction::Append, value_name = "TITLE",  help_heading = "必选参数（数量需一致）")]
        title: Vec<String>,
        /// 电影标签（按顺序对应每部电影，可选，需重复使用-r指定）
        /// 示例：-r "动作" -r "" 表示第1部备注为动作，第2部无备注
        #[arg(short = 'r', long = "remark", action = ArgAction::Append, value_name = "REMARK", help_heading = "可选参数（数量可少于电影数）")]
        remark: Vec<String>,
    },
    /// 退出当前登录状态
    ///
    /// 示例:
    /// - 登出帮助：
    ///   cargo run -- logout -h
    /// - 执行退出登录：
    ///   cargo run -- logout
    ///
    /// 说明：
    /// 1. 退出后需重新登录才能执行查询、添加等操作
    /// 2. 若未登录，执行此命令会提示"未登录状态，无需退出"
    Logout,
}

/// cargo run -- -h
fn main() -> Result<()> {
    let cli = Cli::parse();
    let mut service = Interface::new();
    let _ = match cli.commands {
        // Some(Commands::Login { username }) 这样写就相当于匹配到了传入的参数 username 然后把参数传到 handle_login 里处理就行
        Some(Commands::Login { username }) => match service.handle_login(username) {
            Ok(_) => println!("登录流程完成"),
            Err(e) => eprintln!("登录失败: {}", e), // 必须打印或处理错误
        },
        Some(Commands::Change { path }) => service.handle_change(path)?,
        Some(Commands::Select { disc, disc_op, year, year_op, title, title_op, remark, remark_op, no, no_op, page, size }) => {
            let search = SearchMovie {disc, disc_op, year, year_op, title, title_op, remark, remark_op, no, no_op, page, size};
            let _ = service.handle_select(search)?;
        },
        Some(Commands::Update { disc, disc_op, year, year_op, title, title_op, remark, remark_op, no, no_op, new_disc, new_year, new_title, new_remark }) => {
            let search = SearchMovie { disc, disc_op, year, year_op, title, title_op, remark, remark_op, no, no_op, ..Default::default() };
            let new_remark = new_remark.map(|r| {
                if r.eq_ignore_ascii_case("none") { None } else { Some(r) }
            });
            let modify = ModifyMovie { new_disc, new_year, new_title, new_remark, };
            let count = service.handle_update(search, modify)?;
            println!("成功修改 {} 条记录", count);
        }
        Some(Commands::Delete { disc, disc_op, year, year_op, title, title_op, remark, remark_op, no, no_op }) => {
            let search = SearchMovie { disc, disc_op, year, year_op, title, title_op, remark, remark_op, no, no_op, ..Default::default() };
            let count = service.handle_delete(search)?;
            println!("成功删除 {} 条记录", count);
        }
        Some(Commands::Insert { disc, year, title, remark }) => {
            // 验证参数数量一致性
            let movie_count = disc.len();
            // 处理备注参数（数量可以少于电影总数，不足的用None）
            let mut remarks = remark;
            while remarks.len() < movie_count {
                remarks.push(String::new());
            }
            // 构建电影列表
            // 构建电影列表（映射到 Vec<Movie>）
            let movies: Vec<Movie> = (0..movie_count)
                .map(|i| Movie {
                    disc: disc[i],
                    no: 0, // no 由 disc_add 自动计算，这里暂填 0
                    year: year[i].clone(),
                    title: title[i].clone(),
                    remark: if remarks[i].is_empty() {
                        None // 空字符串表示无备注
                    } else {
                        Some(remarks[i].clone())
                    },
                })
                .collect();
            let count = service.handle_add(movies)?;
            println!("成功添加 {} 条电影记录", count);
        }
        Some(Commands::Logout) => service.handle_logout()?,
        None => {
            return Ok(());
        }
    };
    Ok(())
}
